package bootstrap

import (
	"fmt"
	"io"
	"log/slog"
	"os"
	"strings"
	"time"

	"gopkg.in/natefinch/lumberjack.v2"
)

var lumberjackLogger *lumberjack.Logger

// EarlyLogger 在应用程序启动的最早阶段设置基本的JSON格式日志
// 这个函数应该在main函数的最开始被调用
func EarlyLogger() {
	// 设置一个基本的JSON格式处理器
	opts := slog.HandlerOptions{
		Level: slog.LevelInfo,
		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
			if a.Key == slog.TimeKey {
				return slog.String(slog.TimeKey, a.Value.Time().Format("2006-01-02 15:04:05"))
			}
			return a
		},
	}

	// 创建JSON处理器并设置为默认
	handler := slog.NewJSONHandler(os.Stdout, &opts)
	earlyLogger := slog.New(handler)

	// 添加默认日志字段
	// earlyLogger = earlyLogger.With("module", "APP")

	slog.SetDefault(earlyLogger)
}

// NewLogger 初始化日志系统
func NewLogger(env *Config) *slog.Logger {
	slog.Info("初始化日志...")

	// 设置日志格式(json 或 text)
	var logger *slog.Logger

	// 日志处理器选项
	opts := slog.HandlerOptions{
		AddSource:   env.LOG.ADD_SOURCE,        // 默认不显示源代码位置
		Level:       setupLevel(env.LOG.LEVEL), // 日志级别
		ReplaceAttr: setupTimeFormatter(env),   // 替换源时间格式
	}

	// 日志输出目标, 可以是控制台, 文件或两者的组合
	writer, err := setupOutput(env)
	if err != nil {
		slog.Error("初始化日志失败", slog.String("error", err.Error()))
		return nil
	}

	// 设置日志格式
	if env.LOG.JSON_FORMAT {
		// logger = slog.New(slogdedup.NewOverwriteHandler(slog.NewJSONHandler(writer, &opts), nil))
		logger = slog.New(slog.NewJSONHandler(writer, &opts))
	} else {
		logger = slog.New(slog.NewTextHandler(writer, &opts))
	}

	// 添加默认日志字段
	// logger = logger.With("module", "APP")

	// 如果配置了每日轮转, 启动轮转协程
	if env.LOG.ROTATE_DAILY {
		startDailyRotation()
	}

	slog.Info("日志初始化成功")

	// 设置为默认日志
	slog.SetDefault(logger)

	return logger
}

// 设置输出日志的目标
func setupOutput(env *Config) (io.Writer, error) {
	lumberjackLogger = &lumberjack.Logger{
		Filename:   env.LOG.FILE_NAME,
		MaxBackups: 28,    // 最多保留 28 个备份日志
		MaxAge:     28,    // 最长保留 28 天的日志文件
		MaxSize:    0,     // 最大日志文件大小（MB）, 0 表示不按大小分割
		Compress:   false, // 不压缩旧日志文件
	}

	// 设置日志输出
	switch strings.ToUpper(env.LOG.OUTPUT) {
	// 控制台
	case "CONSOLE":
		return os.Stdout, nil
	// 文件
	case "FILE":
		return lumberjackLogger, nil
	// 控制台和文件
	case "BOTH":
		return io.MultiWriter(os.Stdout, lumberjackLogger), nil
	// 默认控制台
	default:
		return os.Stdout, nil
	}
}

// 根据配置设置日志级别
func setupLevel(level string) slog.Level {
	switch strings.ToUpper(level) {
	case "DEBUG":
		return slog.LevelDebug
	case "INFO":
		return slog.LevelInfo
	case "WARN":
		return slog.LevelWarn
	case "ERROR":
		return slog.LevelError
	default:
		return slog.LevelInfo
	}
}

// 根据配置设置时间格式
func setupTimeFormatter(env *Config) func([]string, slog.Attr) slog.Attr {
	timeFormatFunc := func(t time.Time) string {
		switch strings.ToUpper(env.LOG.TIME_FORMAT) {
		case "HUMAN_READABLE_TIME":
			return t.Format("2006-01-02 15:04:05")
		case "UNIX_TIMESTAMP":
			return fmt.Sprint(t.Unix())
		case "ISO8601_TIME":
			return t.Format("2006-01-02T15:04:05.999999-07:00")
		default:
			return t.Format("2006-01-02 15:04:05")
		}
	}

	return func(groups []string, a slog.Attr) slog.Attr {
		if a.Key == slog.TimeKey {
			t := a.Value.Time()
			return slog.String(slog.TimeKey, timeFormatFunc(t))
		}
		return a
	}
}

// 安排每天日志轮转
func startDailyRotation() {
	if lumberjackLogger != nil {
		go func() {
			for {
				now := time.Now()
				next := time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
				timeUntilNextRotation := time.Until(next)

				slog.Info("设置日志轮转", slog.String("next", next.Format("2006-01-02 15:04:05")))
				t := time.NewTimer(timeUntilNextRotation)
				<-t.C

				err := lumberjackLogger.Rotate()
				if err != nil {
					slog.Error("设置日志轮转失败", slog.String("error", err.Error()))
				}
			}
		}()
	}
}
